#include <sys/timerfd.h>
#include <functional>
#include <cstdint>
#include <memory>
#include <iostream>
#include <assert.h>
#include <unistd.h>
using Task = std::function<void()>;
using ReleaseTask = std::function<void()>;
class TimeTask
{
private:
    Task _time_task;
    ReleaseTask _release_task;
    uint32_t _time_out;
    uint64_t _time_id;
    bool _is_cancel;

public:
    TimeTask(uint32_t timeout, uint64_t timeid, const Task &cb)
        : _time_out(timeout),
          _time_id(timeid),
          _time_task(cb),
          _is_cancel(false)
    {
    }
    ~TimeTask()
    {
        if (_is_cancel == false)
        {
            _time_task();
        }
        _release_task();
    }
    uint64_t GetTimeID()
    {
        return _time_id;
    }
    uint32_t GetTimeOut()
    {
        return _time_out;
    }
    void CancelTask()
    {
        _is_cancel = true;
    }
    void SetRelease(const ReleaseTask &rcb)
    {
        _release_task = rcb;
    }
};
class EventLoop;
class Channel;
class TimeWheel
{
private:
    using WeakPtr = std::weak_ptr<TimeTask>;
    using TaskPtr = std::shared_ptr<TimeTask>;

private:
    std::unordered_map<uint64_t, WeakPtr> _timers;
    unsigned int _tick;
    unsigned int _capacity;
    std::vector<std::vector<TaskPtr>> _wheels;
    EventLoop *_loop;
    int _timerfd;
    std::unique_ptr<Channel> _timer_channel;

private:
    void RemoveTimer(uint64_t id)
    {
        auto it = _timers.find(id);
        if (it != _timers.end())
        {
            _timers.erase(it);
        }
    }
    static int CreateTimeFd()
    {
        // 从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
        int timefd = timerfd_create(CLOCK_MONOTONIC, 0);
        if (timefd < 0)
        {
            assert("TIMEFD创建失败！");
        }
        itimerspec itime;
        itime.it_value.tv_sec = 1;
        itime.it_value.tv_nsec = 0;
        itime.it_interval.tv_sec = 1;
        itime.it_interval.tv_nsec = 0;
        timerfd_settime(timefd, 0, &itime, nullptr);
        return timefd;
    }
    int ReadTimeFd()
    {
        uint64_t res = 0;
        int ret = read(_timerfd, &res, sizeof(res));
        if (ret < 0)
        {
            assert("读取time的文件描述符失败！");
        }
        return res;
    }
    void RunTimeTask()
    {
        _tick = (_tick + 1) % _capacity;
        _wheels[_tick].clear();
    }
    void OnTime()
    {
        int times = ReadTimeFd();
        for (int i = 0; i < times; i++)
        {
            RunTimeTask();
        }
    }
    void TimerAddInLoop(uint64_t id, uint32_t delay, const Task &cb)
    {
        TaskPtr pt(new TimeTask(delay, id, cb));
        pt->SetRelease(std::bind(&TimeWheel::RemoveTimer, this, id));
        int pos = (_tick + delay) % _capacity;
        _wheels[pos].push_back(pt);
        _timers[id] = WeakPtr(pt);
    }
    void TimerRefreshInLoop(uint64_t id)
    {
        auto it = _timers.find(id);
        if (it == _timers.end())
        {
            return;
        }
        TaskPtr pt = it->second.lock();
        int delay = pt->GetTimeOut();
        int pos = (_tick + delay) % _capacity;
        _wheels[pos].push_back(pt);
    }
    void TimerCancelInLoop(uint64_t id)
    {
        auto it = _timers.find(id);
        if (it != _timers.end())
        {
            TaskPtr pt = it->second.lock();
            if (pt)
            {
                pt->CancelTask();
            }
        }
    }

public:
    TimeWheel(EventLoop *loop) : _capacity(60), _tick(0), _wheels(_capacity), _loop(loop),
                                 _timerfd(CreateTimeFd()), _timer_channel(new Channel(_loop, _timerfd))
    {
        _timer_channel->SetReadCallback(std::bind(&TimeWheel::OnTime, this));
        _timer_channel->EnableRead();
    }
    void TimerAdd(uint64_t id, uint32_t delay, const Task &cb)
    {
        _loop->RunInLoop(std::bind(&TimeWheel::TimerAddInLoop, this, id, delay, cb));
    }
    void TimerRefresh(uint64_t id)
    {
        _loop->RunInLoop(std::bind(&TimeWheel::TimerRefreshInLoop, this, id));
    }
    void TimerCancel(uint64_t id)
    {
        _loop->RunInLoop(std::bind(&TimeWheel::TimerCancelInLoop, this, id));
    }
    bool HasTimer(uint64_t id)
    {
        auto it = _timers.find(id);
        if (it != _timers.end())
        {
            return true;
        }
        return false;
    }
};